package com.pay.ioopos.support.scan.s1601;

import static com.pay.ioopos.AppUtils.toast;
import static com.pay.ioopos.Constants.INTENT_PARAM_CASE;
import static com.pay.ioopos.Constants.INTENT_PARAM_CODE;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbManager;
import android.util.Log;

import androidx.lifecycle.LifecycleOwner;

import com.aill.androidserialport.SerialPort;
import com.pay.ioopos.App;
import com.pay.ioopos.ThreadKit;
import com.pay.ioopos.support.scan.ScanCase;
import com.pay.ioopos.support.scan.ScanCaseAbstract;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.Map;
import java.util.concurrent.Future;

/**
 * S1601外接扫码器
 * @author    Moyq5
 * @since  2020/8/17 10:27
 */
public class ScanS1601Qrcode extends ScanCaseAbstract {
    private static final String DEV_PATH = "/dev/ttyACM0";
    private static final int DEV_BAUDRATE = 9600;
    private static final String TAG = ScanS1601Qrcode.class.getSimpleName();
    private static Future<?> future;
    private boolean isRunning = false;
    private final Context context;
    private BroadcastReceiver usbReceiver;

    public ScanS1601Qrcode(Context context) {
        this.context = context;
    }

    @Override
    protected void onStart(LifecycleOwner owner) {
        registerUsbReceiver();
        startService();
    }

    @Override
    protected void onStop(LifecycleOwner owner) {
        unRegisterUsbReceiver(context);
        stopService();
    }

    private void startSerialPort() {
        File file = new File(DEV_PATH);
        if (!file.exists()) {
            return;
        }
        try {
            SerialPort serialPort = new SerialPort(file, DEV_BAUDRATE, 0);
            try (InputStream is = serialPort.getInputStream()) {
                isRunning = true;
                reading(is);
            }
        } catch (Throwable e) {
            if (e instanceof InterruptedException // InterruptedException 可能是取消，
                    || e instanceof SecurityException // SecurityException 可能是没接上设备
                    || e instanceof IOException) {// IOException 可能是在使用过程中插掉设备
            } else {
                Log.e(TAG, "S1601扫码器异常：", e);
                toast("S1601扫码器异常: %s", e.getMessage());
            }
        } finally {
            isRunning = false;
        }
    }

    private void reading(InputStream is) throws InterruptedException, IOException {
        byte[] buffer = new byte[1024];
        while (!Thread.interrupted()) {
            if (is.available() == 0) {
                Thread.sleep(50);
                continue;
            }
            int len = is.read(buffer);
            byte[] data = new byte[len];
            System.arraycopy(buffer, 0, data, 0, data.length);
            String code = new String(data).replaceAll("[\r\n]", "");

            Intent intent = new Intent();
            intent.putExtra(INTENT_PARAM_CASE, ScanCase.QRCODE);
            intent.putExtra(INTENT_PARAM_CODE, code);
            onScan(intent);
        }
    }

    private void registerUsbReceiver() {
        usbReceiver = new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                if (intent.getAction().equals(UsbManager.ACTION_USB_DEVICE_ATTACHED)) {
                    onUsbAttached(context);
                } else if (intent.getAction().equals(UsbManager.ACTION_USB_DEVICE_DETACHED)) {
                    onUsbDettached(context);
                }
            }
        };
        IntentFilter filter = new IntentFilter();
        filter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED);
        filter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED);
        App.getInstance().registerReceiver(usbReceiver, filter);
    }

    private void unRegisterUsbReceiver(Context context) {
        if (null != usbReceiver) {
            try {
                App.getInstance().unregisterReceiver(usbReceiver);
            } catch (Throwable ignored) {

            }
        }
    }

    private void onUsbAttached(Context context) {
        UsbManager manager = (UsbManager) context.getSystemService(Context.USB_SERVICE);
        Map<String, UsbDevice> map = manager.getDeviceList();
        for (UsbDevice device: map.values()) {
            if (isS1601Device(device)) {
                if (!isServiceRunning()) {
                    startService();
                }
                return;
            }
        }
    }

    private void onUsbDettached(Context context) {
        UsbManager manager = (UsbManager) context.getSystemService(Context.USB_SERVICE);
        Map<String, UsbDevice> map = manager.getDeviceList();
        boolean isDettached = true;
        for (Map.Entry<String, UsbDevice> entry: map.entrySet()) {
            if (isS1601Device(entry.getValue())) {
                isDettached = false;
                break;
            }
        }
        if (isServiceRunning() && isDettached) {
            stopService();
        }
    }

    private void startService() {
        if (null != future) {
            future.cancel(true);
        }
        future = ThreadKit.submit(ScanS1601Qrcode.this::startSerialPort);
    }

    private void stopService() {
        isRunning = false;
        if (null != future) {
            future.cancel(true);
            future = null;
        }
    }

    private boolean isServiceRunning() {
        return null != future && !future.isCancelled() && !future.isDone() && isRunning;
    }

    private boolean isS1601Device(UsbDevice device) {
        return (device.getVendorId() == 0x0525 && device.getProductId() == 0xA4A7)
                || (device.getVendorId() == 0x0103 && device.getProductId() == 0x6061)
                || (device.getVendorId() == 0x152A && device.getProductId() == 0x880F);
    }
}
